10. 이름 설계: 구조를 파악할 수 있는 이름

📌 Contents

📌 악마를 불러들이는 이름

  • 포괄적으로 이름을 붙이면 상품과 관련된 모든 로직을 구현하면 될 것처럼 보임
  • 온라인 쇼핑몰에 상품을 그대로 상품 클래스라고 이름을 붙이게 된다면, 상품이 예약, 주문, 발송 등 다양한 관심사에 관한 로직을 갖게 됨
  • 상품 클래스가 다양한 관심사와 결합되어, 온갖 것과 관련된 로직을 품게 되어 강한 결합 구조가 됨
  • 강한 결합을 해소하고, 결합이 느슨하고 응집도가 높은 구조로 만들려면 관심사 분리를 할 수 있어야함
  • 관심사 분리는 "관심사(유스케이스, 목적, 역할)에 따라서 분리한다"라는 소프트웨어 공학의 개념
  • 따라서 상품 클래스를 관심사에 따라서 각각 클래스로 분할해야함
  • 분할된 모든 클래스에 "상품"이라는 이름을 붙일 수 없으니, 관심사에 맞게 이름을 붙여야함
    • 상품 클래스를 예약 상품, 주문 상품, 재고 상품, 발송 상품 등의 클래스로 분할 할 수 있음


  • 개발 초기에는 포괄적인 이름을 붙이는 경우가 많음
  • 의미가 너무 포괄적인 이름은 내부에 온갖 로직을 구현하게 만듬
  • 책의 저자는 이름이 너무 포괄적이라서 목적이 불분명한 클래스를 목적 불명 객체라고 부름
  • 목적 불명 객체는 규모가 커지기 쉬움
  • 이러한 상황에 빠지지 않으려면, 관심사 분리를 고려한 이름을 설계해야 함
  • 관심사 분리를 쉽게 하려면, 비즈니스 목적에 따라 이름을 붙여 보면 됨

📌 이름 설계하기 - 목적 중심 이름 설게

  • 책의 저자는 클래스와 메서드에 이름을 붙이는 것을 '명명'이라고 부르지 않고, 이름 설계라고 부름
  • 여기에서 설계는 '어떤 문제를 해결하기 위한 구조를 생각하거나 만들어 내는 것'을 의미함
  • 프로그래밍에서 이름은 가독성을 높이는 역할만 하는 게 아님
  • 관심사 분리를 생각하고, 비즈니스 목적에 맞게 '이름을 붙이는 것'은 결합이 느슨하고 응집도가 높은 구조를 만드는 데 굉장히 중요한 역할을 함
  • 목적 중심 이름 설계는 목적에 맞게 이름을 설계하는 것
  • 소프트웨어로 달성하고 싶은 목적과 의도를 이름만으로 알 수 있게 하는 것

최대한 구체적이고, 의미 범위가 좁고, 특화된 이름 선택하기

  • 목적 중심 설계에서 가장 중요한 포인트
  • 특정한 목적을 달성하는 데 특화된 의미 범위가 좁은 이름을 클래스에 붙임
  • 그리고 고객 대상 제품 개발에서의 목적은 '회사가 사업적으로 어떤 목적을 달성하고 싶은가'하는 비즈니스 목적이어야 함
  • 비즈니스 목적에 특화하면, 다음과 같은 효과가 생김
    • 이름과 관계없는 로직을 배제하기 쉬워짐
    • 클래스가 작아짐
    • 관계된 클래스 개수가 적으므로, 결합도가 낮아짐
    • 관계된 클래스 개수가 적으므로, 사양 변경 시 생각해야 하는 영향 범위가 좁음
    • 목적에 특화된 이름을 갖고 있으므로, 어떤 부분을 변경해야 할 때 쉽게 찾을 수 있음
    • 개발 생산성이 향상됨

존재가 아니라 목적을 기반으로 하는 이름 생각하기

  • 단순하게 존재를 나타내는 이름은 의미가 여러 곳에서 사용되기 쉬우며, 목적이 불분명해지기 쉬움
  • 결국 로직 레벨에서 혼란을 줌
  • 따라서 구체적인 목적을 알 수 있게, 목적을 기반으로 이름을 짓는 것이 좋음

어떤 비즈니스 목적이 있는지 분석하기

  • 비즈니스 목적에 특화된 이름을 만들려면, 어떤 비즈니스를 하는지 모두 파악해야 함
  • 이를 위해서는 소프트웨어가 추구하는 목적과 내용을 분석해야 함
  • 팀이 모두 모여서 화이트보드나 종이에 그려가며 진행하면 좋음
  • 포스트잇을 사용해 관련된 것끼리 묶어 보면, 생각이 정리될 것임

소리내어 이야기해 보기

  • 사람이 머릿속에서만 생각하는 것은 의외로 굉장히 애매모호함
  • 비즈니스 측면을 잘 알고 있는 사람과 이야기해 보면, 목적과 의도를 잘못 인식하고 있는 부분이 있으면, 곧바로 피드백 받을 수 있음
  • 그리고 이야기를 통해서 좀 더 정확하고 구체적인 비즈니스 목적, 목적에 맞는 이름들을 이끌어 낼 수 있을 것임
  • 프로그래밍에서 어떤 문제가 발생했을 때, 문제를 누군가에게 설명하다 보면 스스로 원인을 깨닫고 해결할 수 있다는 방법인 고무 오리 디버깅이라는 방법이 있음
  • 이처럼 이야기하면서 분석하는 활동을 <<도메인 주도 설계>>(17.1.11절)라는 책에서 유비쿼터스 언어라고 설명함

이용 약관 읽어 보기

  • 이용 약관에는 서비스와 관련된 규칙이 굉장히 엄격한 표현으로 작성되어 있음
  • 그래서 이를 활용하면 비즈니스와 관련된 이름을 알 수 있음

다른 이름으로 대체할 수 없는지 검토하기

  • 열심히 이름을 정했어도 이름의 의미 범위가 생각보다 넓을 수 있고, 여러 의미가 내포된 이름일 수도 있음
  • 그러니 일단 다른 이름으로 바꿔 보고, 의미를 더 좁게 만들 수는 없는지, 이상한 점은 없는지 검토하는 것이 좋음
  • 다른 이름을 생각할 때는 사전에서 유의어를 확인해 보는 것도 도움됨

결합이 느슨하고 응집도가 높은 구조인지 검토하기

  • 목적에 특화된 이름을 선택하면, 목적 이외의 로직을 배제하기 쉬워짐
  • 결과적으로 목적과 관련된 로직이 모이므로, 응집도가 높아짐
  • 목적 이외의 로직이 섞인다면, 이름을 잘못 붙인 것은 아닌지 검토해 보기
  • 클래스가 여러 개의 다른 클래스와 관련되어 있다면, 강한 결합 상태일 수 있음
  • 의미가 더 좁은 특화된 이름을 찾아보고, 여러 의미를 갖고 있다면 분해하기
  • 관련된 클래스의 개수가 적으면 적을수록 영향 범위가 줄어듬

📌 이름 설계 시 주의 사항

이름에 관심 갖기

  • 팀 개발에서는 이름이 중요함
  • 이름과 로직이 대응된다는 전제, 이름이 프로그램 구조를 크게 좌우한다는 사실을 팀원들과 이야기 해야함

사양 변경 시 '의미 범위 변경' 경계하기

  • 개발 중에 계속해서 반복되는 사양 변경에 의해, 개발 맥락에서 말이 의미하는 바가 점점 변화하는 경우가 있음
  • 따라서 이름 설계는 중간중간 다시 검토해 봐야 함
  • 여러 의미가 섞이면, 이름이 의미하는 바를 다시 검토해 봐야함

대화에는 등장하지만 코드에 등장하지 않는 이름 주의하기

  • 실제로 대화에 자주 등장하는 중요한 개념이 소스 코드에서는 이름조차 붙어 있지 않고, 잡다한 로직에 묻혀 있는 경우가 꽤 많음
  • 이를 구현한 사람에게 직접 묻지 않으면, 존재조차 알 수 없고, 존재를 확인해도 로직을 이해하기 힘듬
  • 만약 구현한 사람이 팀을 떠난다면, 문제가 더 커질 수 있음
  • 또한 이와 같은 '이름 없는 로직'은 소스 코드 이곳저곳에 무분별하게 작성되는 경향이 있음
  • 이름이 없다는 것은 메서드 또는 클래스로 설계되어 있지 않다는 의미
  • 이러면 관련된 사양이 변경됐을 때, 소스 코드에 어떠한 이름도 붙어 있지 않으므로, 로직을 찾는 일이 굉장히 힘들 것임

수식어를 붙여서 구별해야 하는 경우는 클래스로 만들어 보기

  • 차이를 구분하기 어려운 코드를 '단순하게 수식어를 붙여서 동료에게 설명하는 상황'은 시스템 개발에서 매우 훈하게 볼 수 있음
  • 예를 들어 게임 개발에서 maxHitPoint가 최대 히트포인트를 나타낸다는 것은 이름을 보고 알 수 있지만, '캐릭터의 원래 최대 히트포인트'인지 '장비 착용으로 높아진 최대 히트포인트'인지는 이름만 보고 절대 알 수 없음
  • 이름을 지은 사람이 팀에서 빠진다면, 무슨 문제가 있는지 로직을 읽어 나가며 유추해야함. 로직이 복잡하면 복잡할수록 유추가 더 어려워짐
  • 따라서 originalMaxHitPoint(캐릭터의 원래 최대 히트포인트), correctedMaxHitPoint(장비 착용으로 높아진 최대 히트 포인트) 등의 이름이 좋음
  • 최대 히트포인트를 단순하게 int 자료형으로 구현하면, 의미 차이를 이름으로만 확인해야 함
  • 또한 여러 관련된 구현이 이곳저곳에 퍼져 응집도가 낮은 구조가 됨
  • 따라서 수식어를 붙이면서까지 차이를 나타내고 싶은 대상은 각각 클래스로 설계하는 것이 좋음
  • 의미가 다른 개념들을 서로 다른 클래스로 설계해서 구조화하면, 개념 사이의 관계를 이해하기 쉬움
  • 수식어를 붙여서 차이를 나타낸다면, 각각을 클래스로 설게할 수 없는지 검토해 보기

📌 의미를 알 수 없는 이름

  • 의미가 분명하지 않은 이름은 이름만 보고는 목적이 무엇인지, 의미를 알기 굉장히 힘듬
  • 목적 중심 이름 설계의 관점에서 보았을 때, 관심사 분리에 아무런 도움도 되지 않음
  • 책무를 알 수 없으며, 강한 결합 구조가 되기 쉬움
  • 이해하기 어려우므로 관련된 부분을 수정할 때마다 코드를 해석해야하고, 새로 들어온 팀원에게 설명하는 비용이 커져서 개발 속도가 느려짐
  • 인간의 주의력에는 한계가 있기 때문에, 부주의로 인해 사양과 로직을 잘못 해석하는 경우가 생길 수 있는데, 의미가 분명하지 않은 이름은 이러한 해석 오류를 더 크게 만듬
  • 이번 절에서는 의미가 분명하지 않은 이름이 되기 쉬운 경우들을 정리함

기술 중심 명명

  • 이름을 프로그래밍과 관련된 용어(function, method, class, module 등)나 컴퓨터와 관련된 용어(memory, cache, thread, register 등), 자료형과 관련된 용어(int, str(string), flag(boolean) 등)에서 유래되는 경우가 많음
  • 이와 같은 기술을 기반으로 이름 짓는 방법을 기술 중심 명명이라고 부름
  • 이렇게 이름을 지으면 이름의 의도를 알기 어려움
  • 이와 같은 경우는 소프트웨어를 구현하는 방법일 뿐, 비즈니스 목적을 나타내는 이름에는 적합하지 않으므로 주의해야 함

로직 구조를 나타내는 이름

  • 메서드 이름에서 로직 구조를 그대로 드러내고 있으면, 무엇을 의도하는지 메서드 이름만 보고는 알기 힘듬
  • 구현하려는 대상이 무엇을 하려는지 제대로 이해하고 있지 못할 떄, 이와 같은 이름을 짓기 쉬움
  • 의도와 목적을 이해하기 쉽게 이름을 붙이자
  • 게임에서 멤버가 마법을 사용할 수 있는 상태인지 판정하는 로직 예제
/**
 * 히트포인트가 0보다 큰가(생존해 있나)?
 * 행동 가능한 상태(canAct)인가?
 * 매직포인트가 남아 있는가?
 */

// 잘못된 예
boolean isMemberHpMoreThanZeroAndIsMemberCanActAndIsMemberMpMoreThanMagicCostMp (Member member) {
  ...
}

// 잘된 예
boolean canEnchant(final Member member) {
  ...
}

놀람 최소화 원칙

  • int count = order.itemCount(); 이 코드를 보면 주문 상품 수를 리턴하는 것 처럼 보임
  • 하지만 itemCount 메서드에서 주문 상품 수를 리턴할 뿐만 아니라, 기프트 포인트까지 추가하고 있다면, itemCount 메서드를 사용했던 사람은 이런 동작까지 예측하지 못했을 터라, 실제로 하고 있는 일을 깨달으면 놀랄 것임
  • 사용자가 예상하지 못한 놀라움을 최소화하도록 설계해야 한다는 접근 방법인 놀람 최소화 원칙(Principle of least astonishment 또는 Rule of least surprise)이라는 원칙이 있음
  • 이를 위해서는 로직과 이름을 잘 대응시켜야 함
  • 처음에는 로직과 의도가 일치하게끔 구현했다고 해도, 사양을 변경하면서 별 생각 없이 기존 메서드에 로직을 추가하는 경우가 있음
  • 이렇게 되면 이름과 로직 사이에 점차 괴리가 생기면서, 놀람 최소화 원칙을 위반할 것임
  • 이는 메서드 레벨에서뿐만 아니라, 클래스 레벨에서도 발생할 수 있는 문제로, 굉장히 흔한 일이므로 주의해야 함
  • 로직을 변경할 때는 항상 놀람 최소화 원칙을 신경 써야 함
  • 로직과 이름 사이에 괴리가 있다면 이름을 수정하거나, 메서드와 클래스를 의도에 맞게 따로 만들자

📌 구조에 악영향을 미치는 이름

데이터 클래스처럼 보이는 이름

  • ~Info나 ~Data 같은 이름의 클래스는 읽는 사람에게 '데이터만 갖는 클래스니까, 로직을 구현하면 안되는 구나' 라는 이미지를 심어줄 수 있음
  • 그러면 응집도가 낮은 구조가 되기 쉬움


  • 예외적으로 데이터 클래스를 사용하는 경우가 있음
  • 변경 책무와 참조 책무를 모듈로 분리하는 명령 쿼리 역할 분리(CQRS)라고 불리는 아키텍처 패턴이 있음
  • CQRS에서 참조 책무란 데이터베이스에서 값을 추출하는 처리로서, 오직 화면 출력만 하면 됨
  • 단순히 값을 추출해서 출력하면 되므로, 계산과 데이터 변경을 동반하지 않음
  • 그래서 데이터베이스의 값을 저장하고, 출력하는 쪽에 전송하기만 하는 클래스를 설계하게 됨
  • 이는 DTO(Data Transfer Object)라고 해서, 데이터 전송 용도로 사용되는 디자인 패턴임
  • 값 변경이 필요 없으므로, 인스턴스 변수는 final로 선언하고 생정자에서 값만 지정함
  • 참조 용도로만 사용되어야 하므로, 값을 변경하는 용도로 사용하면 안됨
  • 만약 변경 용도로 사용하면 응집도가 낮은 구조가 됨

클래스를 거대하게 만드는 이름

  • 대표적으로 Manager라는 이름의 클래스는 여러 책무를 떠안기 쉬워 클래스가 거대해져 갓 클래스가 됨
  • Manager(관리) 라는 단어가 가진 의미가 너무 넓고 애매하기 때문임
  • Manager는 의마가 너무 넓어서 의미가 좁은 개념을 찾아보는 것이 좋음
  • Processor나 Controller 같은 이름도 주의해야 함
  • Controller는 웹 프레임워크에서 MVC 구조에 등장하는 패턴이기도 함
  • MVC에서 Controller는 전달받은 요청 매개변수를 다른 클래스에 전달하는 책무만 가져야함
  • 여기에 금액을 계산하거나, 예약 여부를 판단하는 등의 분기 로직을 구현한다면 단일 책임 원칙을 위반하는 것임
  • 책무가 다른 로직은 다른 클래스로 정의해야함

상황에 따라 의미가 달라질 수 있는 이름

  • 용어는 상황에 따라 의미가 달라짐. ex) Account는 금융 업계에서 '계좌', 컴퓨터 보안에서 '로그인 권한'임
  • 목적과 상황에 따라서 개념이 아예 달라질 수도 있음
  • 상황(컨텍스트)가 다르다면 느슨한 결합으로 설계하는 것이 좋음
  • 한 클래스에 서로 다른 컨텍스트이 강하게 결합되면 안됨
  • 예를 들어 Car 클래스에 배송 컨텍스트와 판매 컨텍스트가 같이 들어가면 안됨
  • 따라서 각 컨텍스트는 서로 다른 패키지로 선언하고 각 패키지에 Car 클래스를 만들어야함

일련번호 명명

  • Class001, method001 처럼 클래스와 메서드의 이름에 번호를 붙여 만드는 것을 일련번호 명명 이라고 부름
  • 목적과 의도를 알기 힘들다는 점에서 기술 중심 명명과 비슷하지만, 구조를 개선하기 훨씬 힘들다는 점에서 훨씬 더 악질적인 방법
  • 기술 중심 명명은 목적 즁심 이름 설계에 따라 이름을 변경하면 됨
  • 일련번호 명명은 여러 클래스와 메서드가 일련번호로 관리되고 있으므로, 일련번호가 아닌 방식으로 이름을 지으면 질서가 무너져 버려서 이름을 수정하기 힘듬
  • 결국 일련번호라는 질서를 유지하기 위해, 기능을 추가할 때 기존의 메서드에 로직을 추가하기 쉬워, 트랜잭션 스크립트 패턴이 되기 쉬움
  • 일반적으로 일련번호로 이름 짓는 방식은 대규모 개발에 사용되어서, 이를 개선하려면 조직 차원에서 논의하고 대처해야 함

📌 이름을 봤을 때, 위치가 부자연스러운 클래스

'동사 + 목적어' 형태의 메서드 이름 주의하기

  • 다양한 애플리케이션에서 관심사와 전혀 관계없는 메서드가 클래스에 추가되는 경우가 많음
  • 관심사가 다른 메서드는 '동사 + 목적어' 형태가 되는 경향이 있음
  • '동사 + 목적어'로 이루어진 이름은 관계없는 책무를 가진 메서드일 가능성이 있음

가능하면 메서드의 이름은 동사 하나로 구성되게 하기

'동사 + 목적어' 형태의 메서드
   ↓
목적어의 개념을 나타내는 클래스를 따로 만듬
그리고 그 클래스에서 '동사 하나' 형태의 메서드를 추가

부적절한 위치에 있는 boolean 메서드

  • '동사 + 목적어' 메서드처럼 boolean 자료형을 리턴하는 메서드도 적절하지 않은 클래스에 정의되어 있는 경우가 많음
  • 관심사에 주의를 기울이지 않으면, boolean 자료형을 리턴하는 판정 매서드는 관계없는 클래스(해당 책무를 갖지 않는 클래스)에 구현되기 쉬움
  • 클래스 is 상태. 형태로 읽어봤을 때 자연스러운 영어 문장이 되면 메서드가 정의되어 있는 클래스 위치가 적절함
Common {
 static boolean isMemberInConfusion(){}
}

Coomon is member in confusion -> 부자연스러움

Member {
  static boolean isConfusion(){}
}

Member is Confusion -> 자연스러움

📌 이름 축약

  • 긴 이름이 싫어서 이름을 축약하면, 의도를 이해하기 힘듬
  • 주석과 문서가 따로 있다면 의도를 알 수 있을 것이지만, 없다면 주변의 로직을 읽어서 무엇을 하는 로직인지 유추해야 해서 시간이 낭비됨
  • 주석이 있었다고 해도, 주석은 유지 보수가 잘되지 않는 내용이 낡은 주석이 되기 쉬워 코드를 이해하기 더 어렵게 할 수도 있음
  • 기본적으로 이름은 축약하지 않는 것이 좋음
  • 변수명뿐만 아니라 메서드 이름, 클래스 이름, 패키지 이름도 축약하지 않고 모두 작성하는 것이 좋음
  • 일반적으로 축약한 이름이 통용된다면 축약해도 괜찮음. SNS, VIP 등의 관습적으로 축약한 형채로 사용하므로 괜찮음

이름을 축약할 수 있는 경우

  • for 반복문의 카운터 변수는 관습적으로 i와 j처럼 짧은 한 글자의 문자로 표현하는 경우가 많음
  • 또한 Go 언어 등의 일부 프로그래밍 언어에서는 짧은 변수 이름을 선호하기도 함
  • 이름을 축약할 때는 의미가 사라지지 않는지, 추가적인 다른 문제가 발생하지는 않는지 등을 꼭 확인하기
  • 프로그래밍 언어에 따라서 관례가 다를 수 있으므로, 이러한 점을 고려해 명명 방법을 팀이나 회사 차원에서 결정해 두는 것이 좋음

❓ Questions

❓ DTO를 변경용도로 사용하면 응집도가 낮아지나?

  • DTO는 참조 용도로만 사용되어야 하므로, 값을 변경하는 용도로 사용하면 안된다고 나온다
  • 변경 용도로 사용하게 된다면 응집도가 낮은 구조가 된다고 하는데, 책에서 말하는 응집도가 낮은 구조는 관련된 로직이 모여있지 않고 분산되있는 것을 뜻하는 것 아닌가?
  • 책의 8절에서 응집도를 높히기 위해 관련없는 로직을 넣으면 강한 결합이 된다고 나온다
  • 그리고 관련이 깊은 로직이 다른 클래스에 가게 되면 응집도가 낮아진다고 한다.
8절
관련이 깊은 데이터와 논리를 한곳에 모은 구조를 응집도가 높은 구조라고 함
높은 응집도를 잘못 이해해서 강한 결합이 발생하는 경우가 있음
관련이 깊을거라 생각해 하나의 클래스에 모았지만, 다른 개념이 섞여 있어 강한 결합에 해당될 수 있음
  • 그러면 DTO를 변경 용도로 사용하게 되면 응집도가 낮아지는게 아니라 강한 결합이 발생하는거 아닌가?
  • 하나의 클래스가 여러 책임을 가져 로직들을 다 가지고 있는 것도 응집도가 낮아진다고 보는것인가?


  • 스터디 시간에 같이 토론하고 검색을 해봤다.
  • A 클래스가 관련 없는 B 클래스의 로직을 갖게 되면 그 로직을 가져야하는 B클래스가 갖지 못해 응집도가 떨어진다고 볼 수 있다.
  • 하지만 애초에 클래스가 A뿐이고 B클래스가 없어 B로직까지 다 가지고 있는 것도 응집도가 떨어지는 것인가?
    • 책에서 '다른 클래스를 확인하고 조작하는 메서드 구조'는 응집도가 낮은 구조라는 말이 있어 이런 경우는 응집도와 관련이 없는 것 아닌가라는 생각을 했었다.
  • 결론은 책에서 '관련 없는 로직을 갖는 것도 응집도가 낮아진 것이다'라는 직접적인 언급은 없었으나 그런 경우도 응집도가 낮다고 하는 것 같다.
    • 책에서는 '관련된 로직이 분산되어 있으면 응집도가 낮아진다'라고 하는데 생각해보면 이는 '관련 없는 로직을 갖는 것도 응집도가 낮아진 것이다'와 비슷한 말인 것 같다.

results matching ""

    No results matching ""